#ifndef __STACK_H__
#define __STACK_H__

#include <stdint.h>
#include <stdlib.h>

#ifndef typeof
#define typeof __typeof__
#endif

#ifndef private
#define private
#endif
/*
 * created by zsy on $date, which is based on list implemation.
 */

struct stack_head {
        struct stack_head *next, *prev;
};

typedef struct stack_context
{
        void *ptr;
        void *arg;
        struct stack_head head;
 }stack_context_t;

#define INIT_STACK(ptr) do { \
        (ptr)->next = (ptr); (ptr)->prev = (ptr); \
} while (0)


private static inline void __stack_add__(struct stack_head *newer,
                              struct stack_head *prev,
                              struct stack_head *next) /*this is not used for public*/
{
        next->prev = newer;
        newer->next = next;
        newer->prev = prev;
        prev->next = newer;
}


static inline void __stack_push(struct stack_head *newer, struct stack_head *head)
{
        __stack_add__(newer, head->prev, head);
}

private static inline void __list_del__(struct stack_head * prev, struct stack_head * next)     /*this is not used for public*/
{
        next->prev = prev;
        prev->next = next;
}

static inline int __stack_empty(const struct stack_head *head)
{
        return head->next == head;
}

/*null if empty*/
static inline struct stack_head *  __stack_pop(struct stack_head *head)
{
        struct stack_head *entry = head->prev;

        if(__stack_empty(head))
                return NULL;
        
        __list_del__(entry->prev, entry->next);

        return entry;
}

#define CONTAINING_RECORD(address, type, field) ((type *)((char*)(address) - (uint64_t)(&((type *)0)->field)))

static inline void stack_init(stack_context_t *stack)
{
        INIT_STACK(&stack->head);
}

static inline void stack_push(stack_context_t *stack, void *ptr)
{
        stack_context_t *entry = (stack_context_t *)malloc(sizeof(stack_context_t));
        entry->ptr = ptr;

        __stack_push(&entry->head, &stack->head);
}

static inline void * stack_pop(stack_context_t * stack)
{
        void *ptr;
        struct stack_head *  _entry = __stack_pop(&stack->head);

        stack_context_t * entry= CONTAINING_RECORD(_entry, stack_context_t, head);
        ptr = entry->ptr;
        free(entry);

        return ptr;
}

static inline int stack_empty(stack_context_t *stack)
{
       return __stack_empty(&stack->head);
}

static inline void stack_push_arg(stack_context_t *stack, void *ptr, void *arg)
{
        stack_context_t *entry = (stack_context_t *)malloc(sizeof(stack_context_t));
        entry->ptr = ptr;
        entry->arg = arg;

        __stack_push(&entry->head, &stack->head);
}

static inline void * stack_pop_arg(stack_context_t * stack, void **arg)
{
        void *ptr;
        struct stack_head *  _entry = __stack_pop(&stack->head);

        stack_context_t * entry= CONTAINING_RECORD(_entry, stack_context_t, head);
        ptr = entry->ptr;
        *arg = entry->arg;

        free(entry);

        return ptr;
}

#endif
